﻿using AdminBase.Model;
using Newtonsoft.Json;
using SqlSugar;
using StackExchange.Redis;
using System.Collections.Concurrent;
using System.Reflection;

namespace AdminBase.Common.Helpers
{
    /// <summary>
    /// redis 帮助类（哨兵模式）
    /// </summary>
    public class RedisHelper : IDisposable
    {
        //连接字符串
        private string _connectionString;
        //实例名称
        private string _instanceName;
        //默认数据库
        private int _defaultDB;
        // 连接对象
        private ConcurrentDictionary<string, ConnectionMultiplexer> _connections;

        private static IDatabase _db;

        /// <summary>
        /// 构造注入
        /// </summary>
        /// <param name="connectionString">连接字符串</param>
        /// <param name="instanceName">实例名</param>
        /// <param name="defaultDB">默认数据库</param>
        public RedisHelper(string connectionString, string instanceName, int defaultDB = 0)
        {
            _connectionString = connectionString;
            _instanceName = instanceName;
            _defaultDB = defaultDB;
            _connections = new ConcurrentDictionary<string, ConnectionMultiplexer>();
            _db = GetDatabase();
        }

        /// <summary>
        /// 获取多路复用器 ConnectionMultiplexer
        /// </summary>
        /// <returns></returns>
        private ConnectionMultiplexer GetConnect()
        {
            return _connections.GetOrAdd(_instanceName, p => ConnectionMultiplexer.Connect(_connectionString));
        }

        /// <summary>
        /// 获取数据库
        /// </summary>
        /// <param name="configName"></param>
        /// <param name="db">默认为0：优先代码的db配置，其次config中的配置</param>
        /// <returns></returns>
        public IDatabase GetDatabase()
        {
            return GetConnect().GetDatabase(_defaultDB);
        }

        public IServer GetServer(string configName = null, int endPointsIndex = 0)
        {
            var confOption = ConfigurationOptions.Parse(_connectionString);
            return GetConnect().GetServer(confOption.EndPoints[endPointsIndex]);
        }

        public ISubscriber GetSubscriber(string configName = null)
        {
            return GetConnect().GetSubscriber();
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Dispose()
        {
            if (_connections != null && _connections.Count > 0)
            {
                foreach (var item in _connections.Values)
                {
                    item.Close();
                }
            }
        }

        #region Key/Value

        /// <summary>
        /// 设置key
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public static bool SetValue(string key, string value, int timeout = 0)
        {
            TimeSpan? expire = timeout > 0 ? new TimeSpan(0, 0, timeout) : null;
            return _db.StringSet(key, value, expire);
        }

        /// <summary>
        /// 获取值
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static string GetValue(string key)
        {
            var value = _db.StringGet(key);
            if (value == RedisValue.Null)
            {
                return string.Empty;
            }
            return value.ToString();
        }

        /// <summary>
        /// 删除key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool DeleteKey(string key)
        {
            return _db.KeyDelete(key);
        }

        /// <summary>
        /// 判断key是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool KeyExists(string key)
        {
            return _db.KeyExists(key);
        }

        /// <summary>
        /// 事务批量删除key
        /// </summary>
        /// <param name="keyList"></param>
        /// <returns></returns>
        public static bool DeleteKeyTrans(IEnumerable<string> keyList)
        {
            var trans = _db.CreateTransaction();
            foreach (var item in keyList)
            {
                trans.AddCondition(Condition.KeyExists(item));
            }

            foreach (var item in keyList)
            {
                trans.KeyDeleteAsync(item);
            }

            return trans.Execute();
        }

        #endregion

        #region Hash

        /// <summary>
        /// 设置hash值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <param name="value"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public static bool SetHash(string key, string field, string value, int timeout = 0)
        {
            if (!_db.HashSet(key, field, value))
            {
                return false;
            }
            if (timeout > 0)
            {
                return _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
            }
            return true;
        }

        /// <summary>
        /// 批量添加到集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public bool SetHashBatch<T>(string key, T entity, int timeout = 0)
        {
            // 获取实体类的类型信息
            Type type = typeof(T);
            // 获取所有字段
            PropertyInfo[] fields = type.GetProperties();
            // 遍历字段并添加
            foreach (PropertyInfo field in fields)
            {
                // 获取字段的值
                object value = field.GetValue(entity);
                if (value == null)
                {
                    continue;
                }
                if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
                {
                    continue;
                }
                string str = value.ToString();
                Type objectType = value.GetType();
                if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(List<>))
                {
                    IEnumerable<object> list;
                    if (value is List<long> longList)
                    {
                        list = longList.Cast<object>().ToList();
                    }
                    else
                    {
                        // 通过反射获取列表中的元素并拼接
                        list = value as IEnumerable<object>;
                    }
                    str = string.Join(",", list.Where(t => t != null && t != ""));
                    if (value == null || string.IsNullOrWhiteSpace(str))
                    {
                        continue;
                    }
                }
                _db.HashSet(key, field.Name, str);
                if (timeout > 0)
                {
                    _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
                }
            }
            return true;
        }

        /// <summary>
        /// 批量获取
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public List<KeyValue> GetHashBatch(string name)
        {
            List<KeyValue> keyValues = new List<KeyValue>();
            // 获取指定键下的所有 Hash 键值对
            var hashEntries = _db.HashGetAllAsync(name);
            // 将 Hash 键值对存储到 List 中
            var hashList = new List<HashEntry>(hashEntries.Result);
            // 如果你只需要键值对的值，可以提取出来
            foreach (var entry in hashList)
            {
                keyValues.Add(new KeyValue
                {
                    Key = entry.Name.ToString(),
                    Value = entry.Value.ToString()
                });
            }
            return keyValues;
        }

        /// <summary>
        /// 获取hash值
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public static string GetHashValue(string key, string field)
        {
            var value = _db.HashGet(key, field);
            if (value == RedisValue.Null)
            {
                return string.Empty;
            }
            return value.ToString();
        }

        /// <summary>
        /// 删除key
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static bool DeleteHash(string key, string field)
        {
            return _db.HashDelete(key, field);
        }

        /// <summary>
        /// 判断hash是否存在
        /// </summary>
        /// <param name="key"></param>
        /// <param name="field"></param>
        /// <returns></returns>
        public static bool HashExists(string key, string field)
        {
            return _db.HashExists(key, field);
        }

        #endregion

        #region List

        /// <summary>
        /// 添加到集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public static bool PushList(string key, string value, int timeout = 0)
        {
            _db.ListRightPush(key, value);
            if (timeout > 0)
            {
                return _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
            }
            return true;
        }

        /// <summary>
        /// 批量添加到集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public static bool BatchPushList(string key, List<string> values, int timeout = 0)
        {
            _db.ListRightPush(key, values.Select(p => new RedisValue(p)).ToArray());
            if (timeout > 0)
            {
                return _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
            }
            return true;
        }

        /// <summary>
        /// 批量添加到集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="values"></param>
        /// <param name="timeout">过期时间（秒，0则不过期）</param>
        /// <returns></returns>
        public static bool BatchPushList<T>(string key, List<T> values, int timeout = 0)
        {
            _db.ListRightPush(key, values.Select(p => new RedisValue(JsonConvert.SerializeObject(p))).ToArray());
            if (timeout > 0)
            {
                return _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
            }
            return true;
        }

        /// <summary>
        /// 获取集合中的数据量
        /// 获取集合
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static List<string>? GetList(string key)
        {
            var values = _db.ListRange(key);
            if (values == null || values.Length == 0)
            {
                return null;
            }

            return values.Select(a => a.ToString()).ToList();
        }

        /// <summary>
        /// 删除并返回集合
        /// </summary>
        /// <param name="key"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static List<string> DeleteList(string key, int count)
        {
            var elements = _db.ListLeftPop(key, count);
            return elements.Select(a => a.ToString()).ToList();
        }

        /// <summary>
        /// 集合数量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long GetListLength(string key)
        {
            return _db.ListLength(key);
        }

        #endregion

        #region Set

        /// <summary>
        /// 获取集合中的数据量
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static long GetSetLength(string key)
        {
            return _db.SetLength(key);
        }

        #endregion

        #region Other

        /// <summary>
        /// 原子性递增
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public static long StringIncrement(string key, int timeout = 0)
        {
            var num = _db.StringIncrement(key);
            if (timeout > 0)
            {
                _db.KeyExpire(key, DateTime.Now.AddSeconds(timeout));
            }
            return num;
        }

        /// <summary>
        /// 是否被锁（并发量小的时候可用）
        /// </summary>
        /// <param name="key"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public static bool IsLock(string key, int timeout = 0)
        {
            TimeSpan? expire = timeout > 0 ? new TimeSpan(0, 0, timeout) : null;
            return !_db.StringSet(key, 1, expire, false, When.NotExists);
        }

        /// <summary>
        /// 分布式加锁
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <param name="timeout"></param>
        /// <returns></returns>
        public static bool LockTake(string key, string value, int timeout = 60)
        {
            TimeSpan expire = timeout > 0 ? new TimeSpan(0, 0, timeout) : new TimeSpan(0, 0, 60);
            return _db.LockTake(key, value, expire);
        }

        /// <summary>
        /// 分布式解锁
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool LockRelease(string key, string value)
        {
            return _db.LockRelease(key, value);
        }

        #endregion
    }
}
